package com.simple.jdbc.core.executor;

import com.simple.jdbc.core.config.BoundSql;
import com.simple.jdbc.core.pojo.Configuration;
import com.simple.jdbc.core.pojo.MappedStatement;
import com.simple.jdbc.core.utils.GenericTokenParser;
import com.simple.jdbc.core.utils.ParameterMapping;
import com.simple.jdbc.core.utils.ParameterMappingTokenHandler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class SimpleExecutor implements Executor {

    private Connection connection = null;

    PreparedStatement preparedStatement = null;

    ResultSet resultSet = null;

    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object param) throws Exception {

        //加载驱动 获取数据库链接
        connection = configuration.getDataSource().getConnection();

        //获取prepareStatement预编译对象
        /**
         * #{}是自定义的占位符  jdbc中是 ？
         * select * from employee where id = #{id} and lastName = #{lastName}
         *
         * select * from employee where id = ？ and lastName = ？
         * <p>
         * 替换过程中 保存#{} 中的值  用来获取参数值
         */
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        String finalSql = boundSql.getFinalSql();

        preparedStatement = connection.prepareStatement(finalSql);

        String parameterType = mappedStatement.getParameterType();
        if (parameterType != null) {
            //反射
            Class<?> parameterTypeClass = Class.forName(parameterType);
            List<ParameterMapping> parameterMappingList = boundSql.getList();
            for (int i = 0; i < parameterMappingList.size(); i++) {
                //param
                String content = parameterMappingList.get(i).getContent();
                //反射取值
                Field declaredField = parameterTypeClass.getDeclaredField(content);
                declaredField.setAccessible(true);
                Object value = declaredField.get(param);
                //赋值
                preparedStatement.setObject(i + 1, value);
                declaredField.setAccessible(false);
            }
        }
        //执行sql 获取返回结果集
        resultSet = preparedStatement.executeQuery();

        //获取返回类 class
        String resultType = mappedStatement.getResultType();
        Class<?> resultTypeClass = Class.forName(resultType);

        List<E> resultList = new ArrayList<>();

        //处理结果集
        while (resultSet.next()) {
            Object result = resultTypeClass.getDeclaredConstructor().newInstance();
            //子弹名 字段值
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                //字段名
                String columnName = metaData.getColumnName(i);
                //字段值
                Object value = resultSet.getObject(columnName);

                //属性描述器 api方法获取某个属性的读写方法
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                //参数：实例对象  要设置的值
                writeMethod.invoke(result, value);
            }
            resultList.add((E) result);
        }

        return resultList;
    }

    /**
     * 替换 #{}
     * 保存 #{} 中的值
     *
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        // 创建标记处理器 配合标记解析器处理解析工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        //创建标记解析器
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);

        String finalSql = genericTokenParser.parse(sql);

        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();

        return new BoundSql(finalSql, parameterMappings);
    }

    /**
     * 释放资源
     */
    @Override
    public void close() {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (preparedStatement != null) {
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
